package com.example.customcrash;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.util.Log;

import java.io.*;
import java.util.Date;

/**
 * Created by Fcy on 2015/10/20.<br/>
 * 1.每次写入前，判断 当前大小 + 要写入的大小 是否<= 5M,如果不超过，直接写入，否则创建新文件<br/>
 * 2.一共有5个文件作为缓冲文件，依次是12345,1满了，存放2，如果都满了，<br/>
 * 3.LogUtils中 PrintStream 成员，logStream = new PrintStream(new
 * FileOutputStream(logFile, true), true); <br/>
 * 4.如果发生crash，将mData写入sd卡log，这时要不要清空mData数据？
 */
public class LogBuffer {
	private static LogBuffer mInstance;

	static boolean initialized = false;

	private static final String KEY_POINT = "log_point";

	private LimitArrayList<String> mData = new LimitArrayList<String>();

	/**
	 * 当前写入的文件
	 */
	private int point = 0;

	/**
	 * 日志格式，形如：[2010-01-22 13:39:1][com.a.c]error occured
	 **/
	private final static String LOG_ENTRY_FORMAT = "[%tF %tT][%s]%s";

	public final static long ONE_M = 1024 * 1024; // 1M
	public final static long ONE_K = 1024; // 1K

	/**
	 * 每个log文件大小
	 */
	private long logSize = 10 * ONE_K; // 字节

	/**
	 * 保存的log文件个数
	 */
	private int logFileCount = 5;

	private SharedPreferences sp;
	private SharedPreferences.Editor editor;

	private PrintStream logStream;

	private LogBuffer() {
	}

	public static LogBuffer getInstance() {
		if (mInstance == null) {
			synchronized (LogBuffer.class) {
				if (mInstance == null) {
					mInstance = new LogBuffer();
				}
			}
		}
		return mInstance;
	}

	public void initOptions(Context context) {
		initOptions(context, 5, 1000, 2 * ONE_K);
	}

	/**
	 * 初始化 log最大个数以及 log文件大小
	 *
	 * @param logCount log最大个数即list最大个数
	 * @param logSize  log文件大小
	 */
	public void initOptions(Context context, int logFileCount, int logCount,
							long logSize) {
		this.logFileCount = logFileCount;
		setLogCount(logCount);
		setLogSize(logSize);

		sp = context.getSharedPreferences(KEY_POINT, Context.MODE_PRIVATE);
		editor = sp.edit();
		// editor.commit();
		getPoint();
	}

	// public int getLogFileCount()
	// {
	// return logFileCount;
	// }
	//
	// public void setLogFileCount(int logFileCount)
	// {
	// if (logFileCount > 0)
	// {
	// this.logFileCount = logFileCount;
	// }
	// else
	// {
	// this.logFileCount = 2;
	// }
	// }

	/**
	 * 设置最大存储log数量，超过将会覆盖，
	 *
	 * @param max 最大存储log数量
	 */
	private void setLogCount(int max) {
		if (max > 0) {
			if (mData != null) {
				mData.setMax(max);
			}
		}
	}

	/**
	 * 设置每个log文件的最大大小
	 *
	 * @param logSize
	 */
	private void setLogSize(long logSize) {
		this.logSize = logSize;
	}

	private String getCurrTime() {
		Date now = new Date();
		return String.format("[%tF %tT]", now, now);
	}

	/**
	 * 添加log信息
	 *
	 * @param log log信息
	 * @param tag Activity、Fragment、Adapter等
	 */
	public void log(String log, String tag) {
		Date now = new Date();
		/** 日志格式，形如：[2010-01-22 13:39:1][com.a.c]error occured **/
		String logs = String.format(LOG_ENTRY_FORMAT, now, now, tag, log);
		mData.add(logs);
	}

	/**
	 * 内存卡是否可用
	 *
	 * @return
	 */
	private boolean isSdCardAvailable() {
		File file = Environment.getExternalStorageDirectory();
		return Environment.getExternalStorageState().equals(
			Environment.MEDIA_MOUNTED) && (file != null && file.exists());
	}

	private File getSdRoot() {
		if (!isSdCardAvailable())
			return null;
		return Environment.getExternalStorageDirectory();
	}

	/**
	 * 获取log存储目录，如果不存在则创建
	 *
	 * @return
	 */
	private File getLogDir() {
		File root = getSdRoot();
		if (root == null)
			return null;
		File dir = new File(root, "homelink");
		if (!dir.exists()) {
			dir.mkdir();
		}
		return dir;
	}

	private void setPoint(int point) {
		if (editor == null) {
			throw new IllegalArgumentException("必须调用initOptions");
		}
		if (point >= logFileCount)
			return;

		this.point = point;
		// 存到sp中
		editor.putInt(KEY_POINT, point);
		editor.commit();
	}

	private void getPoint() {
		if (sp == null) {
			throw new IllegalArgumentException("必须调用initOptions");
		}
		point = sp.getInt(KEY_POINT, 0);
	}


	/**
	 * TODO 初始化创建5个文件作为缓冲，分别为0,1,2,3,4 .log
	 */
	private void init() {
		if (initialized)
			return;

		if (!isSdCardAvailable())
			return;

		for (int i = 0; i < logFileCount; i++) {
			getLogFile(i);
		}
		getPoint();

		try {
			logStream = new PrintStream(
				new FileOutputStream(getLogFile(point), true), true);
			initialized = true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

	}

	public void writeLog() {
		writeLog(true);
	}

	/**
	 * 将log写到文件中
	 *
	 * @param isCrash 是否发生crash，如果是则写入sd卡后清空 mData数据
	 */
	public void writeLog(boolean isCrash) {
		// 每次写入文件时都要判断，因为文件有可能被删除，需要重新创建
		if (!initialized)
			init();
		if (logStream == null || logStream.checkError()) { // fixme 如果文件被删除，需要重新创建文件，然后再写入
			initialized = false;
//			init();
//			writeLog(isCrash);
			return;
		}
		// TODO 将内存中的mData写到sd卡中的log文件中
		// 如果下一个文件>50K，判断已经被写过，那么先清空该文件再写，
		// 1.获取当前操作的文件
		File currFile = getLogFile(point);
		if (currFile == null)
			return;
		getPoint();

		// 2.写之前要判断写入下一条后是否>5M，如果>5M,则stream指向下一个文件
		if (mData.getRealIndex() >= mData.getMax()) { // 写满覆盖
			for (int i = mData.getIndex() + 1; i < mData.getMax(); i++) {
				String log = mData.get(i);
				if (log.getBytes().length + currFile.length() > logSize) {
					// 超过5M，不写入，stream指向下个文件
					resetLogStream();
					currFile = getLogFile(point);
				}
				// 写入该条log信息
				logStream.println(log);
			}
		}

		for (int i = 0; i <= mData.getIndex(); i++) {
			String log = mData.get(i);
			if (log.getBytes().length + currFile.length() > logSize) {
				resetLogStream();
				currFile = getLogFile(point);
			}
			logStream.println(log);
		}

		if (isCrash)
			clearData();
	}

	/**
	 * 写信息
	 *
	 * @param msg
	 */
	public void write(String msg) {
		if (!initialized)
			init();
		if (logStream == null || logStream.checkError()) {
			initialized = false;
			return;
		}
		File currFile = getLogFile(point);
		if (currFile == null)
			return;
		if (msg.getBytes().length + currFile.length() > logSize) { // 超过5M，不写入，stream指向下个文件
			resetLogStream();
			// currFile = getLogFile(point);
		}

		msg = getCurrTime() + msg;
		logStream.println(msg);
	}

	/**
	 * 清空某个文件
	 *
	 * @param file
	 */
	private void clearFile(File file) {
		try {
			FileOutputStream fos = new FileOutputStream(file, false);
			fos.write("".getBytes());
			fos.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 将logStream指向新的log文件
	 */
	private void resetLogStream() {
		point = (point + 1) % logFileCount;
		setPoint(point);
		File logFile = getLogFile(point);
		if (logFile == null) {
			logStream = null;
			return;
		}

		if (logFile.length() > 10) { // 文件已经被写入过，清空文件
			clearFile(logFile);
		}

		try { // 追加、自动刷新
			logStream = new PrintStream(new FileOutputStream(logFile, true),
				true);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	private void clearData() {
		mData.clear();
	}

	/**
	 * 获取对应log文件
	 *
	 * @param i
	 * @return
	 */
	private File getLogFile(int i) {
		if (i >= logFileCount)
			return null;
		File logDir = getLogDir();
		if (logDir == null)
			return null;
		File file = new File(logDir, i + ".log");
		if (!file.exists())
			try {
				// 如果文件不存在，指针归零，是否要清空所有已存在的文件内容
				setPoint(0);
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		return file;
	}
}
